/*
Copyright (c) [2019年5月1日] [吴超]
[MBT_studio] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
		 http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
#include "stdafx.h"
#include "并行.h"

#include "节点/节点树.h"
#include "节点/输入输出节点.h"
#include "节点/插座/C_矢量插座.h"
#include "节点/socket_utils.h"

#include "../../node后端.h"


#include <场景/场景.h>
#include <场景/intern/S_Scene.h>






C_并行节点::C_并行节点(S_设备环境& ctx, C_节点树* link_tree) : C_节点基类(DEF_并行节点, E_节点类型::e_节点Type_并行) {
	f_setWName(u"并行");
	m_Ctx = ctx;
	//m_函数指针 = 0;


	if (link_tree) {
		C_节点基类::f_绑定子集节点树(link_tree);
		m_是否链接 = true;
	}
	else {
		link_tree = f_node_Create并行节点树(false, u"fun_");
		m_是否链接 = false;

		auto* inNode = new C_输入组节点(ctx, 2);
		auto* outNode = new C_输出组节点(ctx);

		inNode->m_插座可编辑 = true;
		outNode->m_插座可编辑 = true;

		link_tree->f_添加节点(inNode);
		link_tree->f_添加节点(outNode);


		inNode->f_add插座(new C_uVec3插座(u"全局线程"), E_插座方向::e_插座Type_输出);
		f_add插座(new C_uVec3插座(u"全局线程"), E_插座方向::e_插座Type_输入);


		C_并行计算环境::f_get(link_tree) = {};
	}
	C_节点基类::f_绑定子集节点树(link_tree);

	m_本地多维线程数量 = f_alloc_iVec3Prop(nullptr, u"本地线程数量");
	m_本地多维线程数量.m_私有 = true;
	f_prop_iVec3(m_本地多维线程数量) = {1,1,1};

	m_本地线程数量 = f_alloc_UI32Prop(nullptr, u"本地线程数量", 1);
	m_本地线程数量.m_私有 = true;

	m_本地线程使用多维 = f_alloc_BoolProp(nullptr, u"多维线程");
	m_本地线程使用多维.m_私有 = true;
	f_prop_Bool(m_本地线程使用多维) = false;


	m_参数 = {};
	f_df_allocGPU参数缓存槽(&m_参数, 2);

	m_参数.m_参数槽[0] = f_scene_get纹理集(f_NodeCtx_get默认场景());
	m_参数.m_参数类型[0] = E_板载缓存类型::e_IMAGE_Array;
	m_参数.m_绑定位置[0] = DEF_TexturesBindID;
	m_参数.m_绑定数量[0] = f_scene_get纹理集数量(f_NodeCtx_get默认场景());

	m_参数.m_参数槽[1] = f_buf_getGlobal().m_全局绑定参数指针;
	m_参数.m_参数类型[1] = E_板载缓存类型::e_SSBO;
	m_参数.m_绑定位置[1] = DEF_BINDING_Render_GlobalParam;
	m_参数.m_绑定数量[1] = 1;
}

C_并行节点::~C_并行节点() {
	f_prop_Release(m_本地多维线程数量);
}

bool C_并行节点::f_update() {
	return false;
}

std::string C_并行节点::DEF_节点编译函数 {
	m_Code = "";
	m_ReleaseCode = "";
	auto name = f_getName();


	std::vector<C_节点基类*> inNodes;
	std::vector<C_节点基类*> outNodes;

	f_get输入节点(this, inNodes);
	f_get输出节点(this, outNodes);

	auto* inNode = inNodes.front();
	auto* outNode = outNodes.front();

	uint32 inNum = inNode->f_get输出插座数量();
	uint32 outNum = outNode->f_get输入插座数量();


	auto& 计算环境 = C_并行计算环境::f_get(m_子集节点树);
	//bool 内联调用 = f_NT_是否内联调用(this);
	bool 编译 = true;
	if (m_子集节点树->m_以编译 & E_节点树编译位标志::e_节点编译_内联调用) {
		编译 = false;
	}

	for (uint32 i = 1; i < inNum; ++i) {
		auto inSocket = f_get输入插座(i);

		switch (inSocket->m_Type) {
			case E_值类型::e_Type_F32_1D:
			case E_值类型::e_Type_Vec2_1D:
			case E_值类型::e_Type_Vec3_1D: {
				inSocket->m_传至GPU = true;
				break;
			}
		}
		
		m_Code += inSocket->f_构建变量代码(0);
	}


	m_常量名称 = "ConstPush_" + name;
	
	if (编译) {
		m_子集节点树->m_以编译 |= E_节点树编译位标志::e_节点编译_内联调用;

		计算环境.m_常量声明类型 = "S_Const_" + f_getName() + "_Push";
		
		计算环境.m_常量定义 = "struct " + 计算环境.m_常量声明类型 + " { \n";
		for (uint32 i = 1; i < inNum; ++i) {
			auto outSocket = inNode->f_get输出插座(i);
			auto inSocket = f_get输入插座(i);
			
			std::string 成员名称 = outSocket->f_getName();
			switch (outSocket->m_Type) {
				case E_值类型::e_Type_I32: {
					计算环境.m_常量定义 += "int ";
					break;
				}
				case E_值类型::e_Type_F32: {
					计算环境.m_常量定义 += "float ";
					break;
				}
				case E_值类型::e_Type_Vec2: {
					计算环境.m_常量定义 += "vec2 ";
					break;
				}
				case E_值类型::e_Type_Vec3: {
					计算环境.m_常量定义 += "vec3 ";
					break;
				}
				case E_值类型::e_Type_iVec3: {
					计算环境.m_常量定义 += "ivec3 ";
					break;
				}
				case E_值类型::e_Type_uVec3: {
					计算环境.m_常量定义 += "uvec3 ";
					break;
				}
				case E_值类型::e_Type_F32_1D:
				case E_值类型::e_Type_Vec2_1D:
				case E_值类型::e_Type_Vec3_1D: {
					计算环境.m_常量定义 += "uint64_t ";
					break;
				}
				case E_值类型::e_Type_纹理: {
					计算环境.m_常量定义 += "uint ";
					break;
				}
			}
			计算环境.m_常量定义 += 成员名称 + ";\n";

			outSocket->f_setCode("const_push." + 成员名称);
		}
		计算环境.m_常量定义 += "};\n";


		


		std::string shader_code = "#version 460 \n";
		shader_code += "#extension GL_GOOGLE_include_directive : enable \n";
		shader_code += "#extension GL_EXT_scalar_block_layout : enable \n";
		shader_code += "#extension GL_EXT_nonuniform_qualifier : enable\n";
		shader_code += "#extension GL_EXT_buffer_reference2 : require \n";
		shader_code += "#extension GL_EXT_shader_explicit_arithmetic_types_int64 : require \n";
		shader_code += "#extension GL_EXT_shader_atomic_float : enable \n";

		shader_code += "#include \"shader_binding_ID.h\" \n";
		shader_code += "#include \"shader_built_in_functions.h\" \n";
		shader_code += "#include \"shader_std.h\" \n";
		shader_code += "#include \"shader_ray.h\" \n";
		shader_code += "#include \"shader_curve.h\" \n";

		//m_Code += "vec4 " + name + " = texture(Array2D_Ts[nonuniformEXT(uint(" + socket1->f_getCode(0) + "))]," + socket2->f_getCode(0) + "); \n";
		shader_code += "layout(buffer_reference, scalar) readonly buffer R_GlobalParameter { S_GlobalParam GlobalParamAddress[]; };\n";
		shader_code += "layout(buffer_reference, scalar) readonly buffer R_GlobalParameter_CurevPoint { S_CurvePoint  curvePointPtr[]; };\n";

		shader_code += "layout(binding = DEF_BINDING_Render_GlobalParam, scalar) readonly buffer B_GlobalParam { S_GlobalParam gGlobalParam; };\n";
		
		
		shader_code += "#include \"shader_sample.h\" \n";
		shader_code += "#include \"shader_noise.h\" \n";
		


		if (f_prop_Bool(m_本地线程使用多维)) {
			uvec3 本地线程数量 = vec_Min(f_df_get本地最大线程(m_Ctx), f_prop_iVec3(m_本地多维线程数量));
			auto local_size_x = f_整数值转字符串(本地线程数量.x);
			auto local_size_y = f_整数值转字符串(本地线程数量.y);
			auto local_size_z = f_整数值转字符串(本地线程数量.z);

			shader_code += "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; \n";
		}
		else {
			uint32 本地线程数量 = DEF_Min(f_def_get本地最大线程数量(m_Ctx), f_prop_UI32(m_本地线程数量));
			auto local_size_x = f_整数值转字符串(本地线程数量);
			//shaderCode += "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; \n";
			shader_code += "layout(local_size_x = " + local_size_x + ", local_size_y = 1, local_size_z = 1) in; \n";
		}
		

		//shaderCode += "layout(binding = DEF_TexturesBindID, rgba32f) uniform image2D Ts[]; \n";
		//shaderCode += "layout(binding = DEF_TexturesBindID, rgba32i) uniform iimage2D Ts[]; \n";
		shader_code += "layout(binding = DEF_TexturesBindID, r32f) uniform image2D Ts[]; \n";
		//shaderCode += "layout(binding = DEF_TexturesBindID_r_f, rgba32f) uniform image1D Ts_rf[]; \n";
		//shaderCode += "layout(binding = DEF_TexturesBindID_r_f, r32f) uniform image2D Ts_rf[]; \n";

		
		shader_code += 计算环境.m_常量定义;

		shader_code += "layout(push_constant, scalar) uniform DRAWPROP_push_constants {\n";
		shader_code += 计算环境.m_常量声明类型 + " const_push; \n";
		shader_code += "};\n";

		shader_code += "void main(){\n";

		//shaderCode += "S_GlobalParam gGlobalParam = R_GlobalParameter(const_push.GlobalParam).GlobalParamAddress[0];\n";
		shader_code += f_代码构建_着色器引用数据声明();
		shader_code += f_NT_编译节点树(m_子集节点树, 定义, 声明);
		shader_code += "}\n";

		for (uint32 i = 1; i < outNum; ++i) {
			auto* 输出节点的输入插座 = outNode->f_get输入插座(i);
			m_Code += 输出节点的输入插座->f_构建变量代码(0);
		}

		std::string 函数名称 = f_str_u16_to_u8(f_getWName());
		auto path = S_节点数据::g_工程文件着色节点代码绝对路径 + 函数名称 + ".comp";
		FILE* f = fopen(path.c_str(), "w");
		if (f) {
			f_file_保存文本(f, shader_code);
			fclose(f);

			auto SPV文件路径 = S_节点数据::g_工程文件着色节点代码绝对路径 + 函数名称 + ".spv";
			int32 err = f_代码构建_编译着色器(path, SPV文件路径);
			if (!err) {
				std::vector<S_常量推送创建参数> 常量 = {
				{E_着色阶段::e_计算着色, 0, sizeof(S_PBF_DiffuseEmission_ConstPush)},
				};
				std::vector<S_着色器创建参数> 参数配置 = {
					{DEF_TexturesBindID, E_着色阶段::e_计算着色, E_板载缓存类型::e_IMAGE_Array},
					{DEF_BINDING_Render_GlobalParam, E_着色阶段::e_计算着色, E_板载缓存类型::e_SSBO},
				};
				计算环境.m_GPU计算 = f_df_创建并行计算(m_Ctx, SPV文件路径, 参数配置, 常量);
			}
			else {
				return "";
			}
		}


		

		//声明 += 计算环境.m_常量定义;
		C_并行计算环境::f_更新代码();
	}
	
	//f_代码构建_内联函数声明定义(name, m_函数声明 + ";\n", m_函数定义);

	
	m_Code += 计算环境.m_常量声明类型 + " " + m_常量名称 + ";\n";
	for (uint32 i = 1; i < inNum; ++i) {
		auto outSocket = inNode->f_get输出插座(i);
		auto inSocket = f_get输入插座(i);

		std::string 成员名称 = outSocket->f_getName();
		switch (outSocket->m_Type) {
			case E_值类型::e_Type_I32: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_F32: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_Vec2: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_Vec3: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_iVec3: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_uVec3: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ";\n";
				break;
			}
			case E_值类型::e_Type_F32_1D: 
			case E_值类型::e_Type_Vec2_1D: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + inSocket->f_getCode(0) + ".m_Mem.m_偏移;\n";
				break;
			}
			case E_值类型::e_Type_纹理: {
				m_Code += m_常量名称 + "." + 成员名称 + " = " + DEF_S(f_JIT_getGPU纹理ID)"(" + inSocket->f_getCode(0) + ");\n";
				break;
			}
		}
	}
	//m_Code += m_常量名称 + ".GlobalParam = " + DEF_S(f_JIT_更新GPU全局参数指针) + "();\n";
	auto 维度 = f_get输入插座(1)->f_getCode(0);

	m_Code += DEF_S(f_JIT_upGPU计算参数)"((S_GPU计算*)" + DEF_指针转字符串(计算环境.m_GPU计算);
	m_Code += ", ((S_GPU参数槽*)" + DEF_指针转字符串(&m_参数) + ") );\n";

	m_Code += 维度 + " = " + DEF_S(vec_Max)"(" + 维度 + ", 1);\n";

	m_Code += DEF_S(f_GPU_run计算)"((S_GPU计算*)" + DEF_指针转字符串(计算环境.m_GPU计算) + ", " + 维度 + ", &" + m_常量名称 + ", sizeof(" + 计算环境.m_常量声明类型 + "), 0);\n";

	
	for (uint32 i = 1; i < outNum; ++i) {
		bool 是否输出插值引用输入插座 = false;
		std::string 输出插座代码 = outNode->f_get输入插座(i)->f_getCode(0);

		for (uint32 socket_id = 1; socket_id < inNum; ++socket_id) {
			std::string 输入插座代码 = inNode->f_get输出插座(socket_id)->f_getCode(0);

			if (输出插座代码 == 输入插座代码) {
				输出插座代码 = f_get输入插座(socket_id)->f_getCode(0);
				是否输出插值引用输入插座 = true;
				break;
			}
		}

		auto* 并行节点输出插座 = f_get输出插座(i);
		if (是否输出插值引用输入插座) {
			并行节点输出插座->f_setCode(输出插座代码);
		}
	}


	if (m_区域父节点) {
		m_区域父节点->m_Code += m_Code;
		//m_区域父节点->m_ReleaseCode.insert(0, 返回参数定义释放);
		return "";
	}
	//释放 += 返回参数定义释放;
	return m_Code;
}

void C_并行节点::f_读取(FILE* f) {
	C_节点基类::f_读取(f);

	S_文件块 文件块 = f_file_读取文件块(f);
	if (文件块.m_块类型 == E_节点工程文件块类型::e_工程文件块_节点实例属性) {
		const uint8* 下一次读取数据指针 = 文件块.m_data;

		if (f_file_版本对比(文件块, S_引擎配置::g_低版本号_0, S_引擎配置::g_文件版本号)) {
			//下一次读取数据指针 = f_prop_Load(m_编译运行, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(m_本地多维线程数量, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(m_本地线程数量, 下一次读取数据指针);
			下一次读取数据指针 = f_prop_Load(m_本地线程使用多维, 下一次读取数据指针);
		}
	}
	free(文件块.m_data);
}

void C_并行节点::f_写入(FILE* f) {
	C_节点基类::f_写入(f);
	auto 文件块 = f_文件块_创建(E_节点工程文件块类型::e_工程文件块_节点实例属性, S_引擎配置::g_文件版本号);
	S_UI8Array* 块 = (S_UI8Array*)f_core_array_new(0, sizeof(uint8));

	//f_prop_Save(m_编译运行, 块);
	f_prop_Save(m_本地多维线程数量, 块);
	f_prop_Save(m_本地线程数量, 块);
	f_prop_Save(m_本地线程使用多维, 块);

	f_file_保存文件块(f, 文件块, 块);
}

void C_并行节点::f_Copy(const C_节点基类* node) {
	
}


C_节点基类* f_node_创建并行节点(S_设备环境& ctx, C_节点树* 子节点树) {
	return new C_并行节点(ctx, 子节点树);
}

C_节点基类* f_node_加载并行节点(S_设备环境& ctx, FILE* f) {
	std::u16string 链接的节点树名称 = f_file_读取字符串(f);

	if (S_节点数据::G用于重新映射节点树到节点的容器.find(链接的节点树名称) == S_节点数据::G用于重新映射节点树到节点的容器.end()) {
		f_node_加载节点树(ctx, f);
	}

	C_并行节点* node = new C_并行节点(ctx, S_节点数据::G用于重新映射节点树到节点的容器[链接的节点树名称]);
	return node;
}

void f_node_保存并行节点(C_节点基类* n, FILE* f) {
	C_并行节点* node = static_cast<C_并行节点*>(n);

	auto* tree = node->f_get子集树();
	f_file_保存字符串(f, tree->m_IDName);

	if (tree->m_未保存或加载) {
		tree->m_未保存或加载 = false;

		std::vector<C_节点树*> trees = { tree };
		f_node_保存节点树(trees, f);
	}
}






C_线程ID节点::C_线程ID节点(S_设备环境& ctx) : C_节点基类(DEF_线程ID节点, E_节点类型::e_节点Type_并行) {
	f_setWName(u"并行");
	m_Ctx = ctx;
	
	DEF_创建uVec3插座_O(u"组ID");
	DEF_创建uVec3插座_O(u"局部ID");

	DEF_创建uVec3插座_O(u"全局ID");

}

C_线程ID节点::~C_线程ID节点() {
	//f_prop_Release(m_本地多维线程数量);
}

std::string C_线程ID节点::DEF_节点编译函数 {
	m_Code = "";
	m_ReleaseCode = "";
	//auto name = f_getName();

	f_get输出插座(1)->f_setCode("gl_WorkGroupID");
	f_get输出插座(2)->f_setCode("gl_LocalInvocationID");
	f_get输出插座(3)->f_setCode("gl_GlobalInvocationID");

	if (m_区域父节点) {
		m_区域父节点->m_Code += m_Code;
		return "";
	}
	return m_Code;
}


void C_线程ID节点::f_读取(FILE* f) {
	C_节点基类::f_读取(f);

	S_文件块 文件块 = f_file_读取文件块(f);
	if (文件块.m_块类型 == E_节点工程文件块类型::e_工程文件块_节点实例属性) {
		const uint8* 下一次读取数据指针 = 文件块.m_data;

		if (f_file_版本对比(文件块, S_引擎配置::g_低版本号_0, S_引擎配置::g_文件版本号)) {
			//下一次读取数据指针 = f_prop_Load(m_本地多维线程数量, 下一次读取数据指针);
		}
	}
	free(文件块.m_data);
}

void C_线程ID节点::f_写入(FILE* f) {
	C_节点基类::f_写入(f);
	auto 文件块 = f_文件块_创建(E_节点工程文件块类型::e_工程文件块_节点实例属性, S_引擎配置::g_文件版本号);
	S_UI8Array* 块 = (S_UI8Array*)f_core_array_new(0, sizeof(uint8));

	//f_prop_Save(m_编译运行, 块);

	f_file_保存文件块(f, 文件块, 块);
}


C_节点基类* f_node_创建线程ID节点(S_设备环境& ctx, C_节点树* 子节点树) {
	return new C_线程ID节点(ctx);
}

C_节点基类* f_node_加载线程ID节点(S_设备环境& ctx, FILE* f) {
	C_线程ID节点* node = new C_线程ID节点(ctx);
	return node;
}

void f_node_保存线程ID节点(C_节点基类* n, FILE* f) {
	C_线程ID节点* node = dynamic_cast<C_线程ID节点*>(n);
}







